import {ContentChildren, QueryList, ViewChildren} from '@angular/core';
import {
  ChangeDetectionStrategy,
  Component, ElementRef,
  Input,
  OnChanges,
  OnDestroy,
  OnInit,
  SimpleChanges,
  TemplateRef,
  ViewEncapsulation,
  AfterViewInit, AfterContentInit
} from '@angular/core';
import { thAddonComponent } from "./th-addon.component";

import {BehaviorSubject, combineLatest, MonoTypeOperatorFunction, Observable, ObservableInput, of, Subject, merge} from "rxjs";
import {filter, map, startWith, switchMap, takeUntil} from "rxjs/operators";

export type TheadItem = {
  text: string,
  hidden: boolean,
  prop: string,
  fixedLeft?: boolean,
  fixedRight?: boolean,
  width?: string,
  min_width?: string,
  sort?: boolean
}

interface CallBackFn {
  filterShowAll?: Function
}

@Component({
  selector: 'app-simple-table',
  templateUrl: './simple-table.component.html',
  styleUrls: ['./simple-table.component.css'],
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class SimpleTableComponent<T> implements OnInit, OnChanges, OnDestroy, AfterViewInit, AfterContentInit {
  destroy$ = new Subject()
  showAll$ = new BehaviorSubject<boolean>(true)
  @Input() align: 'left' | 'center' | 'right' = 'left'
  @Input() showAll = true
  // 分页
  @Input() pageIndex = 1
  @Input() pageSize = 5
  @Input() pageSizeOptions = [5, 10, 20, 30, 40, 50]
  // 数据源
  @Input() dataSource: T[] = []
  @Input() customRow!: TemplateRef<any>
  @Input() thead: TheadItem[] = []
  @Input() callBackFn: CallBackFn = {}
  @ContentChildren(thAddonComponent, { descendants: true }) listOfThComps!: QueryList<thAddonComponent>;
  @ViewChildren('th') ths: QueryList<HTMLElement>
  pageIndex$ = new BehaviorSubject<number>(1)
  pageSize$ = new BehaviorSubject<number>(5)
  dataSource$ = new BehaviorSubject<T[]>([])
  data: T[] = []
  thead$: BehaviorSubject<TheadItem[]> = new BehaviorSubject<TheadItem[]>([])
  currentPageData$: Observable<any> = new Observable<any>()

  constructor(private el: ElementRef) { }

  ngAfterViewInit() {
    this.ths.changes.pipe().subscribe(value => {
      console.log(value)
    })
    const nativeEl = this.el.nativeElement as HTMLElement
    const theadEl = nativeEl.querySelector('thead') as HTMLElement
    const tbodyEl = nativeEl.querySelector('tbody') as HTMLElement
    theadEl.classList.add('ml-table-header')
    tbodyEl.classList.add('ml-table-body')

    const listOfTh$ = this.listOfThComps.changes.pipe(
      startWith(this.listOfThComps)
    ) as Observable<QueryList<thAddonComponent>>
    const filterAreaClick$ = listOfTh$.pipe(
      switchMap(() => merge(...this.listOfThComps.map(th => th.filterAreaClick$))),
      takeUntil(this.destroy$)
    )
    filterAreaClick$.subscribe(thComp => {
      thComp.displayInput$.next(true)
      this.listOfThComps.filter(th => th !== thComp)
        .forEach(t => { t.displayInput$.next(false)})
    })
    const filterInput$ = listOfTh$.pipe(
      switchMap(() => merge(...this.listOfThComps.map(th => th.inputOb$))),
      takeUntil(this.destroy$)
    )
    filterInput$.subscribe(value => {
      console.log(value)
    })
  }

  deepClone<T>(data: T): T
  deepClone(data)
  {
    const res = Array.isArray(data) ? [] : {}
    for (const [k, v] of Object.entries(data)) {
      res[k] = typeof v === 'object' && v !== null && v !== undefined ? this.deepClone(v) : v
    }
    return res
  }

  ngAfterContentInit() {
    const listOfTh$ = this.listOfThComps.changes.pipe(
      startWith(this.listOfThComps)
    ) as Observable<QueryList<thAddonComponent>>
    const thClick$ = listOfTh$.pipe(
      switchMap(() => merge(...this.listOfThComps.map(th => th.thClick$))),
      takeUntil(this.destroy$)
    )

    thClick$.subscribe(thComp => {
      this.listOfThComps.filter(th => th !== thComp)
        .forEach(t => t.resetSortMode())
      // 开始排序
      if (!thComp.tbColumnKey) return
      let sortRes
      let copyDataSource = this.deepClone(this.dataSource)
      const currentSortMode = thComp.currentMode
      switch (currentSortMode) {
        case "sort":
          sortRes = copyDataSource;
          break;
        case "asc":
          sortRes = <Array<any>>copyDataSource.sort((rowFront, rowBefore) => {
            return rowFront[thComp.tbColumnKey] < rowBefore[thComp.tbColumnKey] ? 1 : -1
          });
          break;
        case "desc":
          sortRes = <Array<any>>copyDataSource.sort((rowFront, rowBefore) => {
            return rowFront[thComp.tbColumnKey] > rowBefore[thComp.tbColumnKey] ? 1 : -1
          });
          break
      }
      this.dataSource$.next(sortRes)
      }
    )

  }


  ngOnChanges(changes: SimpleChanges) {
    const { pageIndex, pageSize, dataSource, thead, showAll } = changes
    console.log(changes)
    if (thead) {
      this.thead$.next(thead.currentValue)
    }
    if (dataSource) {
      this.dataSource$.next(dataSource.currentValue)
    }
    if (pageIndex) {
      this.pageIndex$.next(pageIndex.currentValue)
    }
    if (pageSize) {
      this.pageSize$.next(pageSize.currentValue)
    }
    // if (showAll) {
    //   this.showAll$.next(showAll.currentValue)
    // }
  }

  controlDisplay(showAll: boolean) {
    this.thead$.next(
      !showAll ?
       [ ...this.thead.filter(item => !item.hidden)] :
       [ ...this.thead ]
    )
  }

  ngOnInit(): void {
    this.pageIndex$.subscribe(pi => {
      if (pi !== this.pageIndex) {
        this.pageIndex = pi
      }
    })
    this.pageSize$.subscribe(ps => {
      if (ps !== this.pageSize) {
        this.pageSize = ps
      }
    })
    this.currentPageData$ = combineLatest(
      [this.pageIndex$, this.pageSize$, this.dataSource$]
    ) .pipe(
    filter(value => {
      const [pageIndex, pageSize, listOfData] = value;
      const maxPageIndex = Math.ceil(listOfData.length / pageSize) || 1;
      return pageIndex <= maxPageIndex;
    }),
    map(([pageIndex, pageSize, listOfData]) =>
      listOfData.slice((pageIndex - 1) * pageSize, pageIndex * pageSize)),
    takeUntil(this.destroy$),
    )

    combineLatest([this.currentPageData$, this.showAll$])
      .pipe(
        map(value => {
          const [listOfData, isShowAll] = value
          const {filterShowAll} = this.callBackFn
          if (isShowAll) return listOfData
          if (filterShowAll && typeof filterShowAll === "function") {
            return filterShowAll(listOfData, this.thead)
          }
          return listOfData
        }),
        takeUntil(this.destroy$),
      ).subscribe(data => {
      this.data = data
    })


    this.showAll$.subscribe(isShowAll => {
      this.controlDisplay(isShowAll)
    })

  }
  ngOnDestroy() {
    this.destroy$.next()
    this.destroy$.complete()
  }

}
